﻿<title>ConcatenateBlobs | JavaScript</title>

<h1>ConcatenateBlobs | JavaScript - <a href="https://github.com/muaz-khan/ConcatenateBlobs">Source Code</a></h1>

<blockquote>
    You can record multiple blobs; then click "concatenateBlobs" button to concatenate all recorded blobs in single Blob. The resulting "single" Blob can be either played-back locally or pushed to server.
</blockquote>

<style>
button, select {
    font-family: Myriad, Arial, Verdana;
    font-weight: normal;
    border-top-left-radius: 3px;
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
    border-bottom-left-radius: 3px;
    padding: 4px 12px;
    text-decoration: none;
    color: rgb(27, 26, 26);
    display: inline-block;
    box-shadow: rgb(255, 255, 255) 1px 1px 0px 0px inset;
    text-shadow: none;
    background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0.05, rgb(241, 241, 241)), to(rgb(230, 230, 230)));
    font-size: 20px;
    border: 1px solid red;
    outline:none;
}
button:active, button:focus, select:active {
    background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(5%, rgb(221, 221, 221)), to(rgb(250, 250, 250)));
    border: 1px solid rgb(142, 142, 142);
}
button[disabled], select[disabled] {
    background: rgb(249, 249, 249);
    border: 1px solid rgb(218, 207, 207);
    color: rgb(197, 189, 189);
}
blockquote {
    font-size: 20px;
    color: rgb(172, 10, 10);
    border: 1px solid rgb(172, 10, 10);
    padding: 5px 10px;
    border-radius: 5px;
    margin: 9px 10px;
}
span {
    border: 1px dotted red;
    background: yellow;
    padding: 0 5px;
}
</style>

<hr />
<button id="concatenateBlobs" disabled style="float:right;">concatenateBlobs</button>
<button id="startRecording">startRecording</button>
<button id="stopRecording" disabled>stopRecording</button>
<p style="display:inline-block;">
    <span id="output">0 blobs</span> recorded. To concatenate all, click right-side button.
</p>
<hr />

<select>
    <option>Audio</option>
    <option>Video</option>
</select><br><br>
<audio controls data-type="audio/wav"></audio>

<script src="https://www.webrtc-experiment.com/RecordRTC.js" autoplay> </script>
<script src="https://www.webrtc-experiment.com/ConcatenateBlobs.js" autoplay> </script>

<script>
var mediaElement = document.querySelector('audio');

var recordRTC;
var allBlobs = [];
var localMediaStream;
document.querySelector('#startRecording').onclick = function() {
    this.disabled = true;
    document.querySelector('#concatenateBlobs').disabled = true;
    
    navigator.getUserMedia({
        audio: mediaElement.getAttribute('data-type') == 'audio/wav',
        video: mediaElement.getAttribute('data-type') == 'video/webm'
    }, function(stream) {
        localMediaStream = stream;
        
        mediaElement.src = URL.createObjectURL(stream);
        mediaElement.play();

        recordRTC = RecordRTC(stream, {
            type: mediaElement.getAttribute('data-type') == 'audio/wav' ? 'audio' : 'video',
            video: mediaElement
        });
        recordRTC.startRecording();

        document.querySelector('#stopRecording').disabled = false;
    }, function(error) {});
};

document.querySelector('#stopRecording').onclick = function() {
    this.disabled = true;
    recordRTC.stopRecording(function() {
        if(localMediaStream) localMediaStream.stop();
        
        allBlobs.push(recordRTC.getBlob());
        
        var totalBytes = 0;
        allBlobs.forEach(function(blob) {
            totalBytes += blob.size;
        });
        
        output.innerHTML = allBlobs.length + ' blobs (total size: ' + bytesToSize(totalBytes) + ')';
        
        document.querySelector('#startRecording').disabled = false;
        document.querySelector('#concatenateBlobs').disabled = false;
    });
};

document.querySelector('#concatenateBlobs').onclick = function() {
    this.disabled = true;
    document.querySelector('#startRecording').disabled = true;
    document.querySelector('#stopRecording').disabled = true;
    
    var parent = output.parentNode;
    parent.innerHTML =  'Concatenating ' + allBlobs.length + ' blobs.';
    ConcatenateBlobs(allBlobs, mediaElement.getAttribute('data-type'), function(resultingBlob) {
        parent.innerHTML =  'Concatenated. Resulting blob size: <span>' + bytesToSize(resultingBlob.size) + '</span>. Playing-back locally in &lt;audio&gt; tag.';
        
        mediaElement.src = URL.createObjectURL(resultingBlob);
        mediaElement.play();
    });
};

var output = document.querySelector('#output');

// below function via: http://goo.gl/B3ae8c
function bytesToSize(bytes) {
    var k = 1000;
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes === 0) return '0 Bytes';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)), 10);
    return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}

document.querySelector('select').onchange = function() {
    if(this.value == 'Video') {
        var video = document.createElement('video');
        video.setAttribute('data-type', 'video/webm');
        video.setAttribute('controls', 'true');
        mediaElement.parentNode.replaceChild(video, mediaElement);
        mediaElement = video;
    }
    
    if(this.value == 'Audio') {
        var video = document.createElement('audio');
        video.setAttribute('data-type', 'audio/wav');
        video.setAttribute('controls', 'true');
        mediaElement.parentNode.replaceChild(video, mediaElement);
        mediaElement = video;
    }
};
</script>

<h2>How to use?</h2>
<pre>
// https://www.webrtc-experiment.com/ConcatenateBlobs.js
// or: npm install concatenateblobs

// 2nd argument is type of "resulting-blob"
ConcatenateBlobs(<span>[arrayOfBlobs]</span>, <span>'audio/wav'</span>, function(<span>resultingBlob</span>) {

    POST_to_Server(<span>resultingBlob</span>);
    
    // or preview locally
    localVideo.src = URL.createObjectURL(<span>resultingBlob</span>);
});
</pre>
